package admin.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * @author zgb
 * @date 2024-01-26 16:29
 * @description TODO
 */
public class RedisConfig {

    @Value("${spring.redis.host:}")
    private String hostName;

    @Value("${spring.redis.port:}")
    private int port;

    @Value("${spring.redis.password:}")
    private String password;

    @Value("${spring.redis.timeout:}")
    private int timeout;

    @Value("${spring.redis.lettuce.pool.max-idle:}")
    private int maxIdle;

    @Value("${spring.redis.lettuce.pool.min-idle:}")
    private int minIdle;

    @Value("${spring.redis.lettuce.pool.max-wait:}")
    private long maxWaitMillis;

    @Value("${spring.redis.lettuce.pool.max-active:}")
    private int maxActive;

    @Value("${spring.redis.database:0}")
    private int databaseId;

    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory() {

        RedisConfiguration redisConfiguration = new RedisStandaloneConfiguration(
                hostName, port
        );

        // 设置选用的数据库号码
        ((RedisStandaloneConfiguration) redisConfiguration).setDatabase(databaseId);

        if(StringUtils.isNotBlank(password)){
            // 设置 redis 数据库密码
            ((RedisStandaloneConfiguration) redisConfiguration).setPassword(password);
        }

        // 连接池配置
        GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxIdle(maxIdle);
        poolConfig.setMinIdle(minIdle);
        poolConfig.setMaxTotal(maxActive);
        poolConfig.setMaxWaitMillis(maxWaitMillis);

        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder
                = LettucePoolingClientConfiguration.builder()
                .commandTimeout(Duration.ofMillis(timeout));

        LettucePoolingClientConfiguration lettucePoolingClientConfiguration = builder.build();

        builder.poolConfig(poolConfig);

        // 根据配置和客户端配置创建连接
        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfiguration, lettucePoolingClientConfiguration);
        return factory;
    }

    /**
     * springboot2.x 使用LettuceConnectionFactory 代替 RedisConnectionFactory
     * application.yml配置基本信息后,springboot2.x  RedisAutoConfiguration能够自动装配
     * LettuceConnectionFactory 和 RedisConnectionFactory 及其 RedisTemplate
     *
     * @param
     * @return
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        // 设置 ObjectMapper 的可见性，将所有属性设置为可见性，包括私有属性。
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 配置 ObjectMapper，在遇到未知属性时不会抛出异常。
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 激活 ObjectMapper 的默认类型信息，将类型信息作为属性添加到 JSON 字符串中。
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.EVERYTHING,
                JsonTypeInfo.As.PROPERTY);

        // objectMapper.deactivateDefaultTyping(); 这个配置不会添加全类名

        // 配置好的 ObjectMapper 设置到 jsonRedisSerializer 中。
        jsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置 RedisTemplate 的值序列化器为 jsonRedisSerializer。
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        // 设置 RedisTemplate 的键序列化器为 StringRedisSerializer。
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        // 设置 RedisTemplate 的哈希键序列化器为 StringRedisSerializer。
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        // 设置 RedisTemplate 的哈希值序列化器为 jsonRedisSerializer。
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);
        // 对 RedisTemplate 进行必要的初始化。
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }


}
